﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization.Attributes;
using YmtAuth.Domain.DomainService;

namespace YmtAuth.Domain.Model.BlacklistUser
{
    /// <summary>
    /// 黑名单
    /// </summary>
    public class BlackList : Entity<string>
    {
        public DateTime CreateTime { get; private set; }
        public DateTime LastUpdateTime { get; private set; }
        public DateTime ExpiredTime { get; private set; }
        public bool AllowRequest { get; private set; }
        /// <summary>
        /// 0 ：IP，1：设备号
        /// </summary>
        public int Type { get; private set; }

        public BlackList(string requestIp,int type=0)
        {
            this.Id = requestIp;
            this.LastUpdateTime = DateTime.Now;
            this.CreateTime = DateTime.Now;
            this.AllowRequest = false;
            this.Type = type;
        }
        /// <summary>
        /// 当前IP设置为白名单
        /// </summary>
        public void SetWhitelistIp()
        {
            this.LastUpdateTime = DateTime.Now;
            this.ExpiredTime = DateTime.MinValue;
            this.AllowRequest = true;
        }
        /// <summary>
        /// 当前IP设置为黑名单
        /// </summary>
        /// <param name="expiredTime"></param>
        public void SetBlackListIp(TimeSpan expiredTime)
        {
            this.LastUpdateTime = DateTime.Now;
            this.ExpiredTime = DateTime.Now.Add(expiredTime);
            this.AllowRequest = false;
        }

        public void SetBlackListDeviceIdLockdTimeOut(TimeSpan expiredTime)
        {
            this.LastUpdateTime = DateTime.Now;
            this.ExpiredTime = DateTime.Now.Add(expiredTime);
            this.AllowRequest = false;
        }

        /// <summary>
        /// 检查当前IP是被否禁止访问（禁止访问时间是否过期，是否为白名单）
        /// </summary>
        /// <returns></returns>
        public bool IsDisableRequest()
        {
            if (AllowRequest) return false;
            if (ExpiredTime.ToLocalTime() >= DateTime.Now) return true;
            return false;
        }
        public bool CheckLockedTimeIsExpired()
        {
            if (AllowRequest) return false;
            if (ExpiredTime.ToLocalTime() <= DateTime.Now) return true;
            return false;
        }
        public bool IpWhitelistStoreDb(string ipWhitelistStoreDbAppCfg)
        {
            if (string.IsNullOrEmpty(ipWhitelistStoreDbAppCfg)) return true;
            return ipWhitelistStoreDbAppCfg == "1";
        }
        /// <summary>
        ///  检查业务端请求服务，业务端Ip是否为白名单
        /// </summary>
        /// <param name="requestIp"></param>
        /// <returns></returns>
        public bool CheckRequestIpIsWhitelist(string requestIp)
        {
            //检查IP格式
            if (!CheckRequestIpFormat(requestIp)) return false;
            //白名单规则全匹配
            if (IpWhitelistAllMatch(requestIp)) return true;
            //按具体规则匹配
            return IpWhitelistSpecMatch(requestIp);
        }
        public bool CheckRequestIpIsWhitelist()
        {
            //检查IP格式
            if (!CheckRequestIpFormat(this.Id)) return false;
            //白名单规则全匹配
            if (IpWhitelistAllMatch(this.Id)) return true;
            //按具体规则匹配
            return IpWhitelistSpecMatch(this.Id);
        }
        private static bool IpWhitelistSpecMatch(string requestIp)
        {
            if (IpMatchSpec.IpBulackSpecDictionary.Count <= 0) return true;
            try
            {
                return IpMatchSpec.IpBulackSpecDictionary.Any(ipSpec => Match(ipSpec.Key, requestIp, ipSpec.Value.Item1, ipSpec.Value.Item2));
            }
            catch (Exception)
            {
                return false;
            }            
        }
        private static bool IpWhitelistAllMatch(string ip)
        {
            return IpMatchSpec.IpAllMatchDictionary.ContainsKey(ip);
        }
        private static bool Match(string ipSpec, string requestIp, int matchLen, bool isRange)
        {
            if (!isRange)
            {
                return SpecMatch(ipSpec, requestIp, matchLen);
            }
            else
            {
                var ipArraySegment = new ArraySegment<string>(requestIp.Split('.'), 0, matchLen);
                var _listIp = new List<int>();
                for (var i = ipArraySegment.Offset; i < (ipArraySegment.Offset + ipArraySegment.Count); i++)
                    _listIp.Add(ipArraySegment.Array[i].ConvertToInt32(0));
                var _specArray = ipSpec.Split(new char[] { '.' });
                var result = false;
                for (var i = 0; i < _specArray.Length; Interlocked.Increment(ref i))
                {
                    var __spec = _specArray[i].Split(new char[] { '-' });
                    if (__spec.Length == 1)
                    {
                        result = _listIp[i] == _specArray[i].ConvertToInt32(0);
                    }
                    else
                    {
                        result = _listIp[i] >= __spec[0].ConvertToInt32(0) && _listIp[i] <= __spec[1].ConvertToInt32(0);
                    }
                    if (!result)
                    {
                        return false;
                    }
                }
                return result;
            }
        }
        private static bool SpecMatch(string ipSpec, string inputIp, int matchLen)
        {
            var ipArraySegment = new ArraySegment<string>(inputIp.Split('.'), 0, matchLen);
            var list = new List<string>();
            for (var i = ipArraySegment.Offset; i < (ipArraySegment.Offset + ipArraySegment.Count); i++)
                list.Add(ipArraySegment.Array[i]);
            return ipSpec == string.Join(".", list);
        }
        private static bool CheckRequestIpFormat(string requestIp)
        {
            if (string.IsNullOrEmpty(requestIp)
                || requestIp.StartsWith("::")
                || requestIp.Split(new char[] { '.' }).Length != 4) return false;
            return true;
        }
        protected BlackList()
        {
        }
        protected override IEnumerable<object> CompareValues()
        {
            yield return this.Id;
        }
    }
}
